from datetime import datetime
from flask import current_app ,g
from flask_restful.reqparse import RequestParser
from flask_restful import Resource
from toutiao.utils.token import get_token
from models.user import User
from toutiao import db
from toutiao.apps.user.constans import SMS_VERIFICATION_CODE_EXPIRES
from cache.user import UserStatusCache ,UserProfileCache

# 登陆注册
class Login(Resource):
    '''
    需求分析：
        当用户输入手机号和短信验证码之后，会点击登陆
        前端会将手机号和短信验证码以 axios的请求 发送json给后端
        后端就要接收数据
    请求方式/路由：
        POST    /app/v1_0/user
    '''

    def post(self):
        # 1.接收数据，验证数据（验证数据的规则）
        # 1.1 创建验证实例
        parser = RequestParser()
        # 1.2 添加验证参数
        from flask_restful import inputs
        from toutiao.utils.parsers import check_mobile  # 工具包管理正则
        # parser.add_argument('mobile', location='json', required=True, type=inputs.regex('1[3-9]\d{9}'))
        parser.add_argument('mobile', location='json', required=True, type=check_mobile)
        parser.add_argument('code', location='json', required=True, type=inputs.regex('\d{6}'))

        # 1.3 验证,并获取验证之后的数据
        args = parser.parse_args()
        # 1.4 获取数据
        mobile = args.get('mobile')
        code = args.get('code')

        # 2.验证短信验证码
        redis_code = current_app.redis_store.get('app:code:{}'.format(mobile))
        if redis_code is None or redis_code.decode() != code:
            return {'message': '短信验证码错误'}

        # 3.根据手机号查询判断 用户是否注册
        # --进行数据库操作的时候最好要进行异常捕获--
        user = None
        try:
            user = User.query.filter_by(mobile=mobile).first()
        except Exception as e:
            current_app.logger.error(e)

        if user is not None:
            # 4.如果注册过
            user.last_login = datetime.now()
            try:
                db.session.commit()
            except Exception as e:
                current_app.logger.error(e)
                db.session.rollback()
        else:
            # 5.如果没有注册过
            user_id = current_app.id_worker.get_id()
            user = User()
            user.id = user_id
            user.mobile = mobile
            user.name = mobile
            user.last_login = datetime.now()
            # 数据库异常捕获
            try:
                db.session.add(user)
                db.session.commit()
            except Exception as e:
                current_app.logger.error(e)
                db.session.rollback()
                return {'message': 'database error'}, 507  # 507是存储问题
        # 6.生成一个token
        token, refresh_token = get_token(user.id)

        # 缓存用户信息  redis  user_id : string
        # user:{user_id}:profile:string
        UserProfileCache(user.id).save()
        # 用户状态缓存
        UserStatusCache(user.id).save(user.status)
        if not user.status:
            return {'message': '不可用'}, 403

        # 7.返回相应
        return {'token': token,
                'refresh_token': refresh_token
                }

    def put(self):
        # 根据refresh_token 来换取token
        # 不会走我们的 loginrequired装饰器
        # 1. user_id 和 refresh 都要有才正确
        if g.user_id and g.is_refresh_token:

            # 2. 最好先判断一下 用户的状态
            if not UserStatusCache(g.user_id).get():
                return {'message': 'login'}, 403

            # 3. 正确的话 就重写生成一个token
            token, refresh_token = get_token(g.user_id)
            return {'token': token}

        else:
            # 4. user_id 和 refresh 有一个不正确 都返回 403 ，让前端登陆
            return {'message': 'login'}, 403


# 发送短信验证码
class SmsCode(Resource):
    '''
    看原型图
    需求（描述用户行为，前端行为，后端行为）：
        当用户输入手机号之后，点击获取验证码，后端应该发送验证码

    前端已经写死，我们后期基本不分析
    url/http_method:  GET  路径/ 查询字符串
                      POST  form/json
    头条的请求路径 原则上是 不以/结尾， 就是短信验证码特殊
    '''

    def get(self, mobile):
        # 1.获取手机号
        # 2.生产短信验证码
        from random import choice  # 随机选取
        from string import digits  # 是提供了'0-9'10个数,算是10进制
        # 列表推导式 和 join 拼接
        code = ''.join([choice(digits) for _ in range(6)])
        # 3.保存   括号中 key 时间 value
        current_app.redis_store.setex('app:code:{}'.format(mobile), SMS_VERIFICATION_CODE_EXPIRES, code)

        current_app.logger.info(code)  # 通过日志打印下验证码,方便查看
        # 4.返回相应
        return {
            "mobile": mobile
        }


# 查询数据
from toutiao.utils.decorators import loginrequired


class UserInfoResource(Resource):
    method_decorators = [loginrequired]

    def get(self):
        # user = User.query.filter_by(id=1).first()
        #
        # user_data = {
        #     'id': user.id,
        #     'mobile': user.mobile,
        #     'name': user.name,
        #     'photo': user.profile_photo or '',
        #     'is_media': user.is_media,
        #     'intro': user.introduction or '',
        #     'certi': user.certificate or '',
        # }

        # -----先写个固定id------
        user_id = g.user_id
        from cache.user import UserProfileCache
        user_data = UserProfileCache(user_id).get()

        return user_data


import json


# 查询redis缓存
class CacheUserInfoResource(Resource):
    def get(self):
        redis_data = current_app.redis_store.get(1)
        user_data = json.loads(redis_data.decode())
        return user_data

    # 用户第一次将数据库中的数据保存到缓存
    def post(self):
        user = User.query.filter_by(id=1).first()

        user_data = {
            'id': user.id,
            'mobile': user.mobile,
            'name': user.name,
            'photo': user.profile_photo or '',
            'is_media': user.is_media,
            'intro': user.introduction or '',
            'certi': user.certificate or '',
        }

        current_app.redis_store.set(1, json.dumps(user_data))

        return {'message': 'ok'}
